package com.brtbeacon.demo.lot.state;

import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.fragment.app.Fragment;

import com.brtbeacon.demo.lot.LotMapActivity;
import com.brtbeacon.demo.lot.R;
import com.brtbeacon.demo.lot.fragment.ParkingNavigateControlFragment;
import com.brtbeacon.demo.lot.fragment.ParkingNavigateHintFragment;
import com.brtbeacon.locationengine.ble.BRTLocationManager;
import com.brtbeacon.locationengine.ble.BRTPublicBeacon;
import com.brtbeacon.map.map3d.BRTMapView;
import com.brtbeacon.map.map3d.entity.BRTFloorInfo;
import com.brtbeacon.map.map3d.entity.BRTPoi;
import com.brtbeacon.map.map3d.entity.BRTPoint;
import com.brtbeacon.map.map3d.route.BRTDirectionalHint;
import com.brtbeacon.map.map3d.route.BRTMapRouteManager;
import com.brtbeacon.map.map3d.route.BRTRoutePart;
import com.brtbeacon.map.map3d.route.BRTRouteResult;
import com.brtbeacon.map.map3d.route.GeometryEngine;
import com.brtbeacon.map.map3d.utils.BRTConvert;
import com.brtbeacon.mapdata.BRTLocalPoint;
import com.mapbox.geojson.Point;

import java.util.Calendar;
import java.util.List;

import static com.brtbeacon.map.map3d.route.BRTDirectionalHint.BRTRelativeDirection.BRTStraight;

public class ParkingNavigateState extends ParkingState {

    private ParkingNavigateHintFragment fragment_hint;
    private ParkingNavigateControlFragment fragment_controller;

    private double pathCoefficient = 0.0f;
    private BRTLocalPoint lastRealLocalPoint = null;
    private int lastRealLocalPointRestCount = 0;

    private int nextUpDownFloorNumber = Integer.MIN_VALUE;
    private long nextFloorUpDownTimeMillis = 0;
    private long nextFloorUpDownInterval = 10000;

    private Handler handler = new Handler();

    @Override
    public void enter(LotMapActivity activity) {
        super.enter(activity);
        this.activity.setTopFragment(getTopFragment());
        this.activity.setBottomFragment(getBottomFragment());
        if (this.activity.getZoomLevelNavigation() > 0) {
            this.activity.getMapView().setZoom(this.activity.getZoomLevelNavigation());
        }
    }

    @Override
    public void exit() {
        if (this.activity != null) {
            this.activity.setActualLocation(null);
            this.activity.setTopFragment(null);
            this.activity.setBottomFragment(null);

        }
        super.exit();
    }

    private ParkingNavigateHintFragment getTopFragment() {
        if (fragment_hint == null) {
            fragment_hint = ParkingNavigateHintFragment.newInstance();
            fragment_hint.setState(this);
        }
        return fragment_hint;
    }

    private ParkingNavigateControlFragment getBottomFragment() {
        if (fragment_controller == null) {
            fragment_controller = ParkingNavigateControlFragment.newInstance();
            fragment_controller.setState(this);
        }
        return fragment_controller;
    }

    @Override
    public void onFragmentViewCreated(Fragment fragment, View view) {
        super.onFragmentViewCreated(fragment, view);
        if (activity == null)
            return;

        if (fragment == fragment_controller) {
            //TODO...  绑定导航控制界面
            BRTPoi poi = activity.getParkingPoi();
            fragment_controller.tvInfo.setText((poi!=null)?poi.getName():"");
            fragment_controller.btnOK.setOnClickListener(ctrlFragmentOnClickListener);
            fragment_controller.tvChange.setOnClickListener(ctrlFragmentOnClickListener);
        } else if (fragment == fragment_hint) {
            //TODO...  绑定导航路径提示界面
            didUpdateLocation(null, activity.getLastLocation());
        }
    }

    private View.OnClickListener ctrlFragmentOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_ok: {
                    activity.setState(ParkingState.PARKING_STATE_ROUTE);
                    break;
                }

                case R.id.tv_change: {
                    activity.setState(ParkingState.PARKING_STATE_TARGET);
                    break;
                }
            }
        }
    };

    public void onClickAtPoint(BRTMapView mapView, BRTPoint point) {
        //  TODO... DEBUG CODE!!!!!!
        //this.activity.setLocation(BRTConvert.toLocalPoint(point));
        //didUpdateLocation(null, BRTConvert.toLocalPoint(point));
    }

    public void didUpdateLocation(BRTLocationManager BRTLocationManager, BRTLocalPoint localPoint) {

        if (activity.isShowActualLocation()) {
            activity.setActualLocation(localPoint);
        } else {
            activity.setActualLocation(null);
        }

        updatePathCoefficient(localPoint);

        BRTPoint locationPoint = BRTPoint.from(localPoint);
        BRTRouteResult routeResult = activity.getRouteResult();
        boolean isNavEnd = false;

        if (routeResult != null && locationPoint != null) {
            //if (routeResult.isDeviatingFromRoute(locationPoint, 13.5)) {
            if (routeResult.isDeviatingFromRoute(locationPoint, activity.getDistanceToReplanRoute())) {
                activity.speak("已偏航，请沿路线方向前进！", true);
                activity.requestRoute();
            } else {
                BRTPoint nearestPoint = routeResult.getNearestPointOnRoute(locationPoint);
                if (nearestPoint == null) {
                    /**
                     * 定位点不在路径经过楼层，请重新规划路径
                     */
                    activity.speak("定位偏航，重新规划路线！", true);
                    activity.requestRoute();
                } else {
                    //  将定位点设置为路径上最近点，达到路径吸附效果；
                    locationPoint = nearestPoint;
                    BRTRoutePart part = routeResult.getNearestRoutePart(locationPoint);
                    double distance = part.getDistance(locationPoint);
                    //locationPoint = part.getPoint(distance + (activity.getNavMode() == 1?8.0f:0.0f) * pathCoefficient);
                    locationPoint = part.getPoint(distance + (activity.getNavMode() == 1?activity.getForwardDistanceDriving():activity.getForwardDistanceWalking()) * pathCoefficient);

                    /**
                     * 如果定位点距离终点小于设定数值，直接提示到达终点。
                     */
                    double distanceToEnd = activity.getNavMode() == 1?activity.getDistanceToEndDriving():activity.getDistanceToEndWalking();
                    if (routeResult.distanceToRouteEnd(locationPoint) <= distanceToEnd) {
                        activity.setLocation(BRTConvert.toLocalPoint(locationPoint));
                        activity.speak("到达终点附近, 本次导航结束！", true);
                        activity.setState(ParkingState.PARKING_STATE_TARGET);
                        return;
                    }

                    /**
                     *  如果离终点距离变大，只要不超过5米，就不更新定位点保持静止，防止定位点回退。
                     */
                    BRTPoint prevLocationPoint = BRTPoint.from(activity.getLastLocation());
                    if (prevLocationPoint != null && prevLocationPoint.getFloorNumber() == locationPoint.getFloorNumber()) {
                        double prevDistance = routeResult.distanceToRouteEnd(prevLocationPoint);
                        double nextDistance = routeResult.distanceToRouteEnd(locationPoint);
                        if (nextDistance > prevDistance  && nextDistance - prevDistance <= 5)
                            return;
                    }

                    updateNavHintInfo(locationPoint);
                }
            }
        }
        activity.setLocation(BRTConvert.toLocalPoint(locationPoint));

        BRTLocalPoint currentLocation = activity.getLastLocation();
        BRTFloorInfo currentFloorInfo = activity.getCurrentFloor();
        if (currentFloorInfo != null && currentLocation != null && currentFloorInfo.getFloorNumber() != currentLocation.getFloor()) {
            activity.getMapView().setFloorByNumber(currentLocation.getFloor());
        }

        if (activity.getMapView().getRouteResult() == null && !isNavEnd) {
            activity.requestRoute();
        }
    }




    @Override
    public void didRangedLocationBeacons(BRTLocationManager brtLocationManager, List<BRTPublicBeacon> list) {
        super.didRangedLocationBeacons(brtLocationManager, list);
    }

    /**
     * 更新路径前进偏移系数[0..1]；偏移系数 * 偏移最大距离 = 实际位置超前距离
     * @param localPoint 当前定位信息
     */
    private void updatePathCoefficient(BRTLocalPoint localPoint) {
        if (lastRealLocalPoint == null) {
            lastRealLocalPoint = localPoint;
            pathCoefficient = 0.0f;
            lastRealLocalPointRestCount = 3;
        } else {
            if (lastRealLocalPoint.distanceWithPoint(localPoint) <= 3) {
                lastRealLocalPointRestCount += 1;
            } else {
                lastRealLocalPointRestCount = 0;
                lastRealLocalPoint = localPoint;
            }
            if (lastRealLocalPointRestCount >= 4) {
                pathCoefficient -= 0.2f;
            } else {
                pathCoefficient += 0.2f;
            }
            //  限制范围（0.0至1.0）
            pathCoefficient = Math.min(Math.max(pathCoefficient, 0.0), 1.0);
        }
    }

    private void resetPathCoefficient() {
        lastRealLocalPoint = null;
        pathCoefficient = 0.0f;
    }

    private BRTRoutePart lastHintRoutePart = null;
    private int lastDirectionalHintIndex = -1;
    private long lastDirectionalHintTimeMillis = 0;
    private String lastDirectionHintText = null;

    private void processHintSpeak(BRTRoutePart part, List<BRTDirectionalHint> hints, BRTDirectionalHint hint, String content) {
        if (part == null || hints.isEmpty() || hint==null || TextUtils.isEmpty(content)) {
            return;
        }
        long hintTimeMillis = Calendar.getInstance().getTimeInMillis();
        int hintIndex = hints.indexOf(hint);
        long hintElapseTimeMillis = hintTimeMillis - lastDirectionalHintTimeMillis;
        if (lastHintRoutePart == part && lastDirectionalHintIndex == hintIndex) {
            if (content.equals(lastDirectionHintText)) {
                if (hintElapseTimeMillis <= 10000)
                    return;
            }
        }
        activity.speak(content);
        lastHintRoutePart = part;
        lastDirectionalHintIndex = hintIndex;
        lastDirectionalHintTimeMillis = hintTimeMillis;
        lastDirectionHintText = content;
    }

    //  修正车行模式下，过短路径提示左转右转的误导
    private void updateNavHintsForCarMove(List<BRTDirectionalHint> hints) {
        for (BRTDirectionalHint hint: hints) {
            BRTDirectionalHint nextHint = hint.getNextHint();
            if (nextHint == null)
                break;
            if (hint.getLength() <= 10.0) {
                if (hint.getRelativeDirection() == BRTDirectionalHint.BRTRelativeDirection.BRTTurnLeft && nextHint.getRelativeDirection() == BRTDirectionalHint.BRTRelativeDirection.BRTTurnRight) {
                    hint.setRelativeDirection(BRTDirectionalHint.BRTRelativeDirection.BRTLeftForward);
                    nextHint.setRelativeDirection(BRTDirectionalHint.BRTRelativeDirection.BRTRightForward);
                } else if (hint.getRelativeDirection() == BRTDirectionalHint.BRTRelativeDirection.BRTTurnRight && nextHint.getRelativeDirection() == BRTDirectionalHint.BRTRelativeDirection.BRTTurnLeft) {
                    hint.setRelativeDirection(BRTDirectionalHint.BRTRelativeDirection.BRTRightForward);
                    nextHint.setRelativeDirection(BRTDirectionalHint.BRTRelativeDirection.BRTLeftForward);
                }
            }
        }

        if (hints.size() > 2) {
            BRTDirectionalHint endHint = hints.get(hints.size() - 1);
            if (endHint.getLength() <= 8) {
                endHint.setRelativeDirection(BRTDirectionalHint.BRTRelativeDirection.BRTStraight);
            }
        }
    }

    private void updateNavHintInfo(BRTPoint point) {

        if (point == null)
            return;

        BRTRouteResult routeResult = activity.getRouteResult();
        if(routeResult == null)
            return;
        BRTRoutePart part = routeResult.getNearestRoutePart(point);

        if (part == null)
            return;

        //List<BRTDirectionalHint> hints = part.getRouteDirectionalHint();
        List<BRTDirectionalHint> hints = part.getRouteDirectionalHint(4.0, 15);

        BRTDirectionalHint lastHint = hints.get(hints.size()-1);
        BRTDirectionalHint.BRTRelativeDirection lastHintDirection = lastHint.getRelativeDirection();
        double distanceToRouteEnd = routeResult.distanceToRouteEnd(point);
        if (lastHint.getRelativeDirection() == BRTDirectionalHint.BRTRelativeDirection.BRTTurnLeft ||
                lastHint.getRelativeDirection() == BRTDirectionalHint.BRTRelativeDirection.BRTTurnRight) {
            distanceToRouteEnd -= lastHint.getLength();
        }

        int navMode = activity.getNavMode();
        if (navMode == 1) {
            updateNavHintsForCarMove(hints);
        }

        BRTDirectionalHint hint = part.getDirectionalHintForLocationFromHints(point, hints);
        double currentHintRemainLength = getDistanceToHintEnd(hint, point);
        BRTDirectionalHint nextDirectionHint = hint.getNextHint();
        String targetDirectionHintText = hint.getDirectionString();
        BRTDirectionalHint.BRTRelativeDirection targetDirection = hint.getRelativeDirection();


        /*
        double MAX_HINT_REMAIN_LENGTH = navMode==1 ? 28.0 : 10.0;
        double MAX_HINT_PASSED_LENGTH = navMode==1 ? 6.0 : 3.0;
        double MAX_HINT_REMAIN_LENGTH_FOR_NEXT = navMode==1 ? 15.0 : 3.0;
         */
        double MAX_HINT_REMAIN_LENGTH = navMode==1 ? 15.0 : 10.0;
        double MAX_HINT_PASSED_LENGTH = navMode==1 ? 6.0 : 3.0;
        //double MAX_HINT_REMAIN_LENGTH_FOR_NEXT = navMode==1 ? 8.0 : 3.0;
        double MAX_HINT_REMAIN_LENGTH_FOR_NEXT = navMode==1 ? activity.getForwardDistanceSpeakDriving() : activity.getForwardDistanceSpeakWalking();

        if (currentHintRemainLength < MAX_HINT_REMAIN_LENGTH){
            if (nextDirectionHint != null) {
                targetDirection = nextDirectionHint.getRelativeDirection();
                if (currentHintRemainLength < MAX_HINT_REMAIN_LENGTH_FOR_NEXT) {
                    targetDirectionHintText = nextDirectionHint.getDirectionString();
                } else {
                    targetDirectionHintText = "前方" + nextDirectionHint.getDirectionString();
                }
            }
        } else {
            double hintPassedLen = GeometryEngine.distance(Point.fromLngLat(point.getLongitude(), point.getLatitude()), hint.getStartPoint());
            if (hintPassedLen >= MAX_HINT_PASSED_LENGTH) {
                targetDirectionHintText = "保持前行";
                targetDirection = BRTStraight;
            }
        }

        //  上下楼层提示
        double MAX_HINT_UP_DOWN_LENGTH = navMode==1 ? 20.0 : 15.0;
        if (!part.isLastPart()) {
            BRTFloorInfo currFloorInfo = part.getFloorInfo();
            BRTFloorInfo nextFloorInfo = part.getNextPart().getFloorInfo();
            long now = Calendar.getInstance().getTimeInMillis();
            if (nextUpDownFloorNumber == currFloorInfo.getFloorNumber() && now - nextFloorUpDownTimeMillis <= nextFloorUpDownInterval)
                return;
            if (currFloorInfo.getFloorNumber() != nextFloorInfo.getFloorNumber()) {
                double remainLength = GeometryEngine.distance(Point.fromLngLat(point.getLongitude(), point.getLatitude()), part.getLastPoint());
                if (remainLength < MAX_HINT_UP_DOWN_LENGTH) {
                    String hintNextUpDown = "前方";
                    if (currFloorInfo.getFloorNumber() < nextFloorInfo.getFloorNumber()) {
                        hintNextUpDown += "上" + nextFloorInfo.getFloorNumber() + "层";
                    } else {
                        hintNextUpDown += "下" + nextFloorInfo.getFloorNumber() + "层";
                    }
                    activity.speak(hintNextUpDown);
                    nextUpDownFloorNumber = currFloorInfo.getFloorNumber();
                    nextFloorUpDownTimeMillis = now;
                }
            }
        }


        processHintSpeak(part, hints, hint, targetDirectionHintText);






        //  即将到达终点，提示终点方位。
        if (distanceToRouteEnd <= 10) {
            /*
            targetDirectionHintText += ", 到达终点附近。";
            if (lastHintDirection == BRTDirectionalHint.BRTRelativeDirection.BRTTurnLeft) {
                targetDirectionHintText += ", 到达终点附近。";
            } else if (lastHintDirection == BRTDirectionalHint.BRTRelativeDirection.BRTTurnRight) {
                targetDirectionHintText += ", 到达终点附近。";
            }
            */
            if (lastHintDirection == BRTDirectionalHint.BRTRelativeDirection.BRTTurnLeft) {
                activity.speak("即将到达，终点在左侧");
            } else if (lastHintDirection == BRTDirectionalHint.BRTRelativeDirection.BRTTurnRight) {
                activity.speak("即将到达，终点在右侧");
            } else {
                activity.speak("即将到达, 终点在前方");
            }
        }

        if (fragment_hint.isVisible()) {

            fragment_hint.getTvHint().setText(targetDirectionHintText);

            TextView tvRemainLength = fragment_hint.getTvRemainLength();
            TextView tvHint = fragment_hint.getTvHint();
            ImageView ivDirection = fragment_hint.getIvDirection();
            tvRemainLength.setText(String.valueOf((long) routeResult.distanceToRouteEnd(point)) + "米");
            tvHint.setText(targetDirectionHintText);

            switch (targetDirection) {
                case BRTStraight: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_up);
                    break;
                }
                case BRTTurnRight: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_turn_right);
                    break;
                }
                case BRTRightForward: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_right_up);
                    break;
                }
                case BRTLeftForward: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_left_up);
                    break;
                }
                case BRTRightBackward: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_right_down);
                    break;
                }
                case BRTLeftBackward: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_left_down);
                    break;
                }
                case BRTTurnLeft: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_turn_left);
                    break;
                }
                case BRTBackward: {
                    ivDirection.setImageResource(R.drawable.bb_arrow_back);
                    break;
                }
                default:
                    ivDirection.setImageDrawable(null);
            }
        }
        activity.getMapView().lookAt(point, hint, 500);
    }

    private double getDistanceToHintEnd(BRTDirectionalHint hint, BRTPoint point) {
        return GeometryEngine.distance(Point.fromLngLat(point.getLongitude(), point.getLatitude()), hint.getEndPoint());
    }


    /*
    private long animTimeIntervalMillis = 200;

    private void startAnimTimer(long intervalMillis, long delayMillis) {
        handler.removeCallbacks(animRunable);
        animTimeIntervalMillis = intervalMillis;
        handler.postDelayed(animRunable, delayMillis);
    }

    private void stopAnimTimer() {
        handler.removeCallbacks(animRunable);
    }

    private Runnable animRunable = new Runnable() {
        @Override
        public void run() {
            BRTRouteResult result = activity.getRouteResult();
            if (result != null) {

            }
            handler.postDelayed(animRunable, animTimeIntervalMillis);
        }
    };
    */

    @Override
    public void didSolveRouteWithResult(BRTMapRouteManager routeManager, BRTRouteResult routeResult) {
        super.didSolveRouteWithResult(routeManager, routeResult);
        /*
        double deviceHeading = activity.getLastDeviceHeading();
        if (activity.getLastLocation() == null)
            return;
        BRTPoint point = BRTPoint.from(activity.getLastLocation());
        BRTRoutePart part = routeResult.getNearestRoutePart(BRTPoint.from(activity.getLastLocation()));
        if (part == null)
            return;
        List<BRTDirectionalHint> hints = part.getRouteDirectionalHint(4.0, 15);
        BRTDirectionalHint hint = part.getDirectionalHintForLocationFromHints(point, hints);
        */
        //activity.speak("", true);
        /*
        double angleDiff = Math.abs(hint.getCurrentAngle() - (deviceHeading - 90));
        if (angleDiff > 90) {

        }
        System.out.print(hint.getCurrentAngle() + ", " + deviceHeading + ", angleDiff = " + angleDiff);
        */
        double deviceHeading = activity.getLastDeviceHeading();
        if (activity.getLastLocation() == null)
            return;

        BRTPoint point = BRTPoint.from(activity.getLastLocation());
        BRTRoutePart part = routeResult.getNearestRoutePart(BRTPoint.from(activity.getLastLocation()));
        if (part == null)
            return;
        List<BRTDirectionalHint> hints = part.getRouteDirectionalHint(4.0, 15);
        BRTDirectionalHint hint = part.getDirectionalHintForLocationFromHints(point, hints);
        if (hint == null)
            return;

        System.out.println(String.format("%#.2f, %#.2f", hint.getCurrentAngle(), headingToHintAngle(deviceHeading)) );

        double hintDeviceHeading = headingToHintAngle(deviceHeading);
        double diffAngle = Math.abs(hintDeviceHeading - hint.getCurrentAngle());
        if (diffAngle>= 90 && diffAngle <= 270) {
            activity.speak("请调头！", true);
        }
    }

    private double headingToHintAngle(double newHeading) {
        double heading = 90 - newHeading;
        if (heading >= 180) {
            heading += -360;
        }
        return heading;
    }
}
